/**
 * Subject 接口声明了一组用于管理订阅者的方法。
 */
interface Subject {
    // 将观察者附加到主题上。
    attach(observer: Observer): void;

    // 将观察者从主题上分离。
    detach(observer: Observer): void;

    // 通知所有观察者有关事件的信息。
    notify(): void;
}

/**
 * Subject 拥有一些重要的状态，并在状态发生变化时通知观察者。
 */
class CustomConcreteSubject implements Subject {
    /**
     * @type {number} 为了简单起见，主题的状态，对所有订阅者都重要，存储在这个变量中。
     */
    public state: number;

    /**
     * @type {Observer[]} 订阅者列表。在实际生活中，订阅者列表可以更全面地存储（按事件类型分类等）。
     */
    private observers: Observer[] = [];

    /**
     * 订阅管理方法。
     */
    public attach(observer: Observer): void {
        const isExist = this.observers.includes(observer);
        if (isExist) {
            return console.log('Subject: 观察者已经被附加。');
        }

        console.log('Subject: 附加了一个观察者。');
        this.observers.push(observer);
    }

    public detach(observer: Observer): void {
        console.log(`Subject: 观察者数量:${this.observers.length}`);
        const observerIndex = this.observers.indexOf(observer);
        if (observerIndex === -1) {
            return console.log('Subject: 不存在的观察者。');
        }

        this.observers.splice(observerIndex, 1);
        console.log(`Subject: 分离了一个观察者。观察者数量:${this.observers.length}`);
    }

    /**
     * 在每个订阅者中触发更新。
     */
    public notify(): void {
        console.log('Subject: 通知观察者...');
        for (const observer of this.observers) {
            observer.update(this);
        }
    }

    /**
     * 通常，订阅逻辑只是主题实际可以做的一部分。主题通常持有一些重要的业务逻辑，
     * 当某些重要事件即将发生（或之后）时触发通知方法。
     */
    public someBusinessLogic(): void {
        console.log('\nSubject: 我正在做一些重要的事情。');
        this.state = Math.floor(Math.random() * (10 + 1));

        console.log(`Subject: 我的状态刚刚改变为: ${this.state}`);
        this.notify();
    }
}

/**
 * Observer 接口声明了由主题使用的更新方法。
 */
interface Observer {
    // 从主题接收更新。
    update(subject: Subject): void;
}

/**
 * Concrete Observers 对它们所附加到的 Subject 发出的更新作出反应。
 */
class ConcreteObserverA implements Observer {
    public update(subject: Subject): void {
        if (subject instanceof CustomConcreteSubject && subject.state < 3) {
            console.log('ConcreteObserverA: 对事件作出反应。');
        }
    }
}

class ConcreteObserverB implements Observer {
    public update(subject: Subject): void {
        if (subject instanceof CustomConcreteSubject && (subject.state === 0 || subject.state >= 2)) {
            console.log('ConcreteObserverB: 对事件作出反应。');
        }
    }
}

/**
 * 客户端代码。
 */

const subject = new CustomConcreteSubject();

const observer1 = new ConcreteObserverA();
subject.attach(observer1);

const observer2 = new ConcreteObserverB();
subject.attach(observer2);

subject.someBusinessLogic();
subject.someBusinessLogic();
console.log("移除观察者2");
subject.detach(observer2);

subject.someBusinessLogic();
